package cn.zyl.common.redis.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * MiniRedisOutputStream 类说明: 该类实现缓冲输出流而不进行同步还有一些特殊操作，如就地字符串编码。
 * @author wsz
 * @version v1.0
 * @date 2020/8/7
 */
public final class MiniRedisOutputStream extends FilterOutputStream {
    protected final byte[] buf;
    protected int count;
    private final static int SIZE = 8192;
    private final static int[] SIZE_TABLE = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999,
            999999999, Integer.MAX_VALUE };
    private final static byte[] DIGIT_TENS = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1',
            '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2',
            '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4',
            '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6',
            '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8',
            '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', };
    private final static byte[] DIGIT_ONES = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8',
            '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
            '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };
    private final static byte[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
            'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
            't', 'u', 'v', 'w', 'x', 'y', 'z' };
    private final static int N_0 = 0;
    private final static int N_1 = 1;
    private final static int N_2 = 2;
    private final static int N_3 = 3;
    private final static int N_5 = 5;
    private final static int N_6 = 6;
    private final static int N_16 = 16;
    private final static int N_100 = 100;
    private final static int N_65536 = 65536;
    private final static int N_52429= 52429;
    private final static byte C_R = '\r';
    private final static byte C_N = '\n';
    private final static byte C__ = '-';
    public MiniRedisOutputStream(final OutputStream out) {
        this(out, SIZE);
    }

    public MiniRedisOutputStream(final OutputStream out, final int size) {
        super(out);
        if (size <= N_0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    private void flushBuffer() throws IOException {
        if (count > N_0) {
            out.write(buf, N_0, count);
            count = N_0;
        }
    }
    public void write(final byte b) throws IOException {
        if (count == buf.length) {
            flushBuffer();
        }
        buf[count++] = b;
    }
    @Override
    public void write(final byte[] b) throws IOException {
        write(b, N_0, b.length);
    }
    @Override
    public void write(final byte[] b, final int off, final int len) throws IOException {
        if (len >= buf.length) {
            flushBuffer();
            out.write(b, off, len);
        } else {
            if (len >= buf.length - count) {
                flushBuffer();
            }

            System.arraycopy(b, off, buf, count, len);
            count += len;
        }
    }
    public void writeCrLf() throws IOException {
        if (N_2 >= buf.length - count) {
            flushBuffer();
        }
        buf[count++] = C_R;
        buf[count++] = C_N;
    }
    public void writeIntCrLf(int value) throws IOException {
        if (value < N_0) {
            write(C__);
            value = -value;
        }
        int size = N_0;
        while (value > SIZE_TABLE[size]) {
            size++;
        }
        size++;
        if (size >= buf.length - count) {
            flushBuffer();
        }
        int q, r;
        int charPos = count + size;
        while (value >= N_65536) {
            q = value / N_100;
            r = value - ((q << N_6) + (q << N_5) + (q << N_2));
            value = q;
            buf[--charPos] = DIGIT_ONES[r];
            buf[--charPos] = DIGIT_TENS[r];
        }
        while (value > N_0) {
            q = (value * N_52429) >>> (N_16 + N_3);
            r = value - ((q << N_3) + (q << N_1));
            buf[--charPos] = DIGITS[r];
            value = q;
        }
        count += size;
        writeCrLf();
    }
    @Override
    public void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
}
